import { Svg } from '@svgdotjs/svg.js'
import { clkStore, runtimeConfig } from '../config/runtimeConfig'
import {
    BoolType,
    IDataValue,
    IHasCenterObj,
    IModuleWithState,
    IPoint,
} from '../types'
import anime from 'animejs'

/**
 * 根据svg对象获取dataURL
 * @param svg svg对象
 * @returns 返回dataURL
 */
export function getDataURL(svg: Svg) {
    return 'data:image/svg+xml;base64,' + window.btoa(svg.svg())
}

/**
 * 形状的边类型
 */
type SideType = 'left' | 'right' | 'top' | 'bottom'

/**
 * 获取中点的坐标
 * @param shape 形状
 * @param side 获取哪条边的中点，若未传递则获取整个图形的中点
 * @returns 返回中点坐标
 */
export function getCenterPoint(
    shape: IHasCenterObj,
    side: SideType | 'default' = 'default'
): IPoint {
    if (shape.center && shape.center[side]) {
        return {
            x: shape.center[side]!.x,
            y: shape.center[side]!.y,
        }
    }
    const { width, height, x, y } = shape
    switch (side) {
        case 'left':
            return { x: x, y: y + height / 2 }
        case 'right':
            return { x: x + width, y: y + height / 2 }
        case 'top':
            return { x: x + width / 2, y: y }
        case 'bottom':
            return { x: x + width / 2, y: y + height }
        default:
            return { x: x + width / 2, y: y + height / 2 }
    }
    // return [NaN, NaN]
}

/**
 * 设置css属性
 */
export function setCssVariable(prop: string, value: string) {
    if (!/^--/.test(prop)) {
        prop = '--' + prop
    }
    document.documentElement.style.setProperty(prop, value)
}

/**
 * querySelector的简写
 */
export function $(sel: string, root?: HTMLElement): HTMLElement | null {
    if (root) {
        return root.querySelector(sel) as HTMLElement | null
    }
    return document.querySelector(sel) as HTMLElement | null
}

/**
 * querySelectorAll的简写
 */
export function $A(sel: string, root?: HTMLElement): NodeListOf<HTMLElement> {
    if (root) {
        return root.querySelectorAll(sel)
    }
    return document.querySelectorAll(sel)
}

/**
 * 设置dom中的bool值
 */
export async function setDomBoolVal(prop: string, value: BoolType) {
    const dom = $(`.${prop}.bool`)
    if (!dom) {
        console.warn(`设置布尔项${prop}时失败，未找到元素`)
        return
    }
    dom.dataset.boolVal = +value + ''
}

/**
 * 给布尔值注册点击事件
 */
export function registerBoolClickEvent(module: IModuleWithState<any>) {
    const keys = Object.keys(module.state)
    keys.forEach((prop) => {
        const dom = $(`.${prop}.bool`)
        if (!dom) {
            // console.warn(
            //     `注册布尔项${module.name}.${prop}的点击事件时失败，未找到元素`
            // )
            return
        }
        dom.addEventListener('click', () => {
            if (clkStore.isAnimating || clkStore.isRunning) {
                return
            }
            module.setState(prop, +!module.state[prop])
        })
    })
}

/**
 * 转换成二进制字符串
 */
export function toBinaryStr(
    num: number | string,
    len = 8,
    hasSuffix = false
): string {
    if (typeof num === 'string') {
        return num
    }
    const radix = runtimeConfig.dataDisplayRadix
    let res = num.toString(radix)
    if (radix === 2) {
        res = res.padStart(len, '0')
        if (hasSuffix) {
            res = '0b' + res
        }
    }
    if (radix === 16) {
        res = '0x' + res
    }
    return res
}

/**
 * 设置dom中的dataItem的值
 */
export async function setDomDataItemVal(sel: string, value: number) {
    const dom = $(sel)
    if (!dom) {
        console.warn(`设置${sel}时失败，未找到元素`)
        return
    }
    dom.dataset.value = value + ''
    dom.textContent = toBinaryStr(value)
}

/**
 * createDataValue的辅助函数
 */
function setPosition(this: any, point: IPoint) {
    if (!this.dom) {
        return
    }
    this.dom.style.transform = `translate(-50%, 0) translateX(${point.x}px) translateY(${point.y}px)`
}
function setText(this: any, text: string) {
    if (!this.dom) {
        return
    }
    const oldText = this.dom.textContent
    this.dom.textContent = text
    if (oldText !== text) {
        // 闪烁动画
        anime({
            targets: this.dom,
            backgroundColor: 'rgba(254, 217, 152, 1)',
            duration: 100,
            direction: 'alternate',
            easing: 'easeOutCubic',
            loop: 2,
            complete: () => {
                this.dom.style.backgroundColor = 'rgba(255, 255, 255, 0.5)'
            },
        })
    }
    if (this.dom.dataset.value) {
        const num = toInt255(text)
        this.value = num
        this.dom.dataset.value = num + ''
    }
}
/**
 * 生成一个BUS中的数据
 */
export function createDataValue<T extends string | number>(
    value: T,
    text: string,
    initialPosition: IPoint = { x: 0, y: 0 }
): IDataValue<T> {
    const dom = document.createElement('div')
    dom.textContent = text
    dom.classList.add('data-item-val')
    dom.style.transform = `translate(-50%, 0) translateX(${initialPosition.x}px) translateY(${initialPosition.y}px)`
    $('#bus-container')!.appendChild(dom)
    dom.dataset.value = value + ''
    return {
        value: value,
        dom,
        setPosition,
        setText,
    }
}

/**
 * 机器报错
 * @param message 错误信息
 */
export function VSPMError(message: string) {
    console.error(message)
}

/**
 * 获取二进制的某一位
 */
export function binaryAt(value: number, index: number): BoolType {
    return ((value >> index) & 1) as BoolType
}

/**
 * 转换成一个255以内的整数
 */
export function toInt255(value: string): number {
    // 如果是空字符串，说明是要输入0
    if (value === '') {
        return 0
    }
    value = value.trim()
    let num = parseInt(value)
    // 如果是0b开头或者以b结尾，说明是二进制
    if (value.startsWith('0b') || value.endsWith('b')) {
        value = value.replace('b', '')
        num = parseInt(value, 2)
    }
    // 如果是0x开头或者以h结尾，说明是十六进制
    if (value.startsWith('0x') || value.endsWith('h')) {
        value = value.replace('h', '')
        num = parseInt(value, 16)
    }
    if (isNaN(num)) {
        // 如果是大小写字母，则转换成对应的ASCII码
        if (/[a-zA-Z]/.test(value)) {
            num = value[value.length - 1].charCodeAt(0)
        }
    }
    if (isNaN(num)) {
        return 0
    }
    // 如果是负数，取绝对值，大于255直接取后8位
    if (num > 255) {
        num &= 0xff
    } else if (num < 0) {
        num *= -1
    }
    return num
}
